﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelAddIn1
{
    class Forecasting_MovingAverage : Test
    {
        public Forecasting_MovingAverage(DataContainer data)
        {
            this.data = data;
            res = new List<String>();
        }

        public override string GetInfo()
        {
            String s = "Forecasts a dataset using Simple Moving Average with 2, 3 and 4 periods.";
            return s;
        }

        public override void RunTest()
        {
            res.Add("Simple Moving Average");

            //Error check
            if (data.GetNoSets() != 1)
            {
                res.Add("One dataset is required!");
                return;
            }

            //Error check 2
            if (data.GetDataSet(0).GetN() < 3)
            {
                res.Add("Sample size of minimum 3 is required!");
                return;
            }

            int bestP = 2;
            double bestMAD = 0.0;
            for (int p = 2; p <= 4; p++)
            {
                if (p < data.GetDataSet(0).GetN())
                {
                    //Find the best MAD value
                    double cMAD = RunTest(p, false);
                    if (cMAD < bestMAD || bestMAD == 0.0)
                    {
                        bestP = p;
                        bestMAD = cMAD;
                    }
                }
            }

            res.Add("Periods;Forecast;MAD;-");

            //Re-run the best forecast to output result.
            RunTest(bestP, true);

            res.Add(";;;-");

        }
         
        public double RunTest(int periods, bool show)
        {
            DataSet d = data.GetDataSet(0);
            
            double Fc = CalcFc(periods, d);
            double MAD = CalcMAD(periods, d);
            
            if (show) res.Add(periods + ";" + Fc.ToString("F2") + ";" + MAD.ToString("F2"));

            return MAD;
        }

        private double CalcFc(int periods, DataSet d)
        {
            return CalcFc(periods, d.GetN(), d);
        }

        private double CalcFc(int periods, int row, DataSet d)
        {
            double Fc = 0.0;

            for (int r = row - periods; r < row; r++)
            {
                Fc += d.GetValue(r);
            }

            Fc = Fc / (double)periods;

            return Fc;
        }

        private double CalcMAD(int periods, DataSet d)
        {
            //Create table
            double[,] tab = new double[d.GetN(), 4];
            for (int r = 0; r < d.GetN(); r++)
            {
                //Actual
                tab[r, 0] = d.GetValue(r);
                if (r >= periods)
                {
                    //Forecast
                    tab[r, 1] = CalcFc(periods, r, d);
                    //Error
                    tab[r, 2] = tab[r, 0] - tab[r, 1];
                    //Absolute error
                    tab[r, 3] = Math.Abs(tab[r, 2]); 
                }
            }

            //Calculate Mean Absolute Deviation
            double MAD = 0.0;
            int n = 0;
            for (int r = periods; r < d.GetN(); r++)
            {
                MAD += tab[r, 3];
                n++;
            }
            MAD = MAD / (double)n;

            return MAD;
        }
    }
}
